<!--
 * @Author: lirl
 * @Date: 2022-07-18 10:15:53
 * @Description: 公共流程表单组件
-->
<template>
  <div :set="(defineObj = customTemplateConfig.defineComponentObj)">
    <component
      :is="customTemplateConfig.templateComponent"
      ref="templateRef"
      :task-id="taskId"
      :piid="processInstanceId"
      :document-title="documentTitle"
      :document-title-color="customTemplateConfig.formTitleColor"
      :biz-task-title="processTitle"
      :process-definition-key="processDefinitionKey"
      :flush-portal-url="flushPortalUrl"
      :item-id="itemId"
      :unique-id="uniqueId"
      :maintain-field="maintainField"
      :attachment-remark="attachmentRemark"
      :secret="secret"
      :tree-id="treeId"
      :biz-save-function="bizSaveFunction"
      :biz-before-click-submit-btn="bizBeforeClickSubmitBtn"
      :biz-before-click-form-submit-btn="bizBeforeClickFormSubmitBtn"
      :biz-before-path-next="bizBeforePathNext"
      :biz-before-task-submit="bizBeforeTaskSubmit"
      :biz-after-task-submit="bizAfterTaskSubmit"
      :biz-before-click-cancel="bizBeforeClickCancel"
      :biz-before-cancel="bizBeforeCancel"
      :biz-after-cancel="bizAfterCancel"
      :biz-after-close="bizAfterClose"
      :biz-confirm-submit-event="bizConfirmSubmitEvent"
      :biz-cancel-submit-event="bizCancelSubmitEvent"
      :get-biz-form-data="getBizFormData"
      :path-var-data="pathVarData"
      :candidate-var-data="candidateVarData"
      :download-types="downloadTypes"
      :print-types="printTypes"
      :attachment-area-space-code="customTemplateConfig.attachmentAreaSpaceCode"
      :upload-request-headers="uploadRequestHeaders"
      :doc-data="docData"
      :biz-process-template-attrs="settings.templateAttrs"
      :biz-process-submit-attrs="settings.processSubmitAttrs"
      :biz-process-attrs="settings.process"
      :other-datas="otherDatas"
      :biz-form-data="bizFormData"
      :biz-on-check-path="bizOnCheckPath"
      :biz-before-submit-param="bizBeforeSubmitParam"
      :prohibit-duplicate-number="customTemplateConfig.prohibitDuplicateNumber"
      :hide-opinion-area="customTemplateConfig.hideOpinionArea"
      :number-other-fields="customTemplateConfig.numberOtherFields"
      :close-window-after-submit="false"
      :extend-collapse="extendCollapse"
      :collapse-list="customTemplateConfig.collapseList"
      :bizOpinionAttrs="settings.opinionAttrs"
      :defineTopComponentShow="customTemplateConfig.defineTopComponentShow"
      :defineComponentObj="defineObj"
      @afterLoadFormSubmit="afterLoadFormSubmit"
      @afterLoadDocEditor="afterLoadDocEditor"
      @afterLoadDocPage="afterLoadDocPage"
      @setAllFieldsReadonly="setAllFieldsReadonly"
      @saveNumber="saveNumber"
      @commonEvent="templateCommonEvent"
      @doAction="doAction"
      @processCellClick="getUserInfo"
      @templateInit="templateInit"
      @switchBusinessCode="switchBusinessCode"
    >
      <!-- 头部插槽 -->
      <template v-if="!isEmpty(defineObj)" v-slot:titleArea="defineSlotProps">
        <component
          :is="defineObj.component"
          v-if="defineObj.component"
          ref="defineComponent"
          :task-info="defineSlotProps.taskInfo"
          :is-done="defineSlotProps.isDone"
          :node-id="defineSlotProps.nodeId"
          :task-type="defineSlotProps.taskType"
          :formData="formDataForExtendCollapse"
        ></component>
      </template>
      <template v-slot:baseInfo="slotProps">
        <component
          :is="subFormComponent"
          v-if="!isEmpty(slotProps.configData)"
          ref="subform"
          v-bind="slotProps"
          :process-definition-key="processDefinitionKey"
          :process-title.sync="processTitle"
          :document-form-title.sync="documentTitle"
          is-new-submit
          :formAttrs="formAttrs"
          :validate-by-alert="validateByAlert"
          :all-fields-readonly="allFieldsReadonly"
          :login-user-info="{ treeId, telphone, loginName }"
          :need-save-datas="needSaveDatas"
          :extend-collapse-validate-fn="extendCollapseValidateFn"
          @changeSecret="changeSecret"
          @changeAttachmentRemark="changeAttachmentRemark"
          @pathVarData="getPathVarData"
          @candidateVarData="getCandidateVarData"
          @setDocData="setDocData"
          @commonEvent="commonEvent"
          @afterLoadForm="afterLoadSubForm"
          @changeBizFormData="changeBizFormData"
        ></component>
      </template>
      <!-- 扩展组件 -->
      <template v-for="item in extendCollapse" :slot="item.slot" slot-scope="slotProps">
        <component
          :is="item.component"
          ref="extendCollapse"
          :key="item.slot"
          :formData="formDataForExtendCollapse"
          :task-info="slotProps.taskInfo"
          :nodeId="slotProps.nodeId"
          :isDone="slotProps.isDone"
          :form-field-config="slotProps.formFieldConfig"
          :is-to-read="slotProps.isToRead"
          :is-maintain="slotProps.isMaintain"
          :all-fields-readonly="allFieldsReadonly"
          :configData="configData"
          :process-definition-key="processDefinitionKey"
          @change="changeExtendCollapseData"
        ></component>
      </template>
    </component>
    <jdf-render-dialog v-if="renderVisible" :visible.sync="renderVisible" :item="renderConfig"></jdf-render-dialog>
  </div>
</template>

<script>
import {
  isEmpty,
  cloneDeep,
  isObject,
  defer,
} from 'lodash-es';
import { PermissionFilterService } from 'jdf-jssdk';
import { MessageBox } from 'element-ui';
import { closeWindow, isEmptyStr } from 'jdf-util';
import { bizServiceURL } from '@/api/utils/constants';
import { templateNames } from '@/utils/enum';
import settings from '@/settings/settings';
import sendMixn from './sendMixn';
import subFormConfig from './subFormConfig.json';
import processFormCommonMixin from './processFormCommonMixin';

export default {
  mixins: [sendMixn, processFormCommonMixin],
  async beforeRouteEnter(to, from, next) {
    const { path, query: { piid, maintainField } } = to;
    if (!piid || maintainField === 'maintenance-toolbox') {
      next();
      return;
    }
    try {
      // path 等于 /process-form-oa 时进行判断
      if (path === '/process-form') {
        // 有权限则直接next，无权限则弹框提示没有权限
        await new PermissionFilterService(bizServiceURL).validateAuth(piid);
        next();
      }
    } catch (error) {
      MessageBox.alert('无权限访问', '提示', {
        type: 'warning',
        callback: () => {
          closeWindow();
        },
      });
    }
  },
  data() {
    const { $route: { query } } = this;
    return {
      isEmpty,
      // 待办id
      taskId: query.taskId,
      // 流程模板id
      processDefinitionKey: query.processDefinitionKey,
      // 回刷地址
      flushPortalUrl: query.flushPortalUrl,
      // 待办项唯一标识
      itemId: query.itemId,
      // 统一待办唯一标识
      uniqueId: query.uniqueId,
      // 是否运维页面
      maintainField: query.maintainField,
      // 流程实例id
      processInstanceId: query.piid,
      // 回文id
      pmsid: query.pmsid,
      // 全局配置
      settings,
      // 表单标题
      documentTitle: '',
      // 加载的子组件
      subFormComponent: null,
      // 表单属性
      formAttrs: {
        // 隐藏校验不通过时的红色信息
        'show-message': false,
        // 隐藏必填的星号
        'hide-required-asterisk': false,
      },
      // 自定义业务表单模板配置
      customTemplateConfig: {},
      renderVisible: false,
      renderConfig: {},
      // 流程全量配置
      configData: {},
      // 表单提交组件提供的一些方法
      formSubmitFuncs: {},
      // 模板提供的方法
      templateMethods: {},
      // 自定义扩展区域
      extendCollapse: null,
      // 传给扩展组件的formData
      formDataForExtendCollapse: {},
    };
  },
  computed: {
  },
  mounted() {
    this.loadSubComponent();
    // 设置表单标题
    this.documentTitle = templateNames[this.processDefinitionKey];
  },
  methods: {
    /**
     * 流程模版初始化后事件
     * @param {Object} data 表单配置对象，包含以下属性
     * formOperateConfig 表单操作栏配置信息
     * formFieldConfig 表单字段配置信息
     * formConfig 表单配置,包含表单元素配置(分组形式展示)
     * formOpinionConfig 表单意见域配置
     * opinionDecisionConfig 决策性意见
     * formAttachConfig 表单附件域配置
     * processInstance 流程实例信息
     * processTask 流程任务信息
     * attachResource 附件资源
     * formOpinionList 意见列表
     * docFlowConfig 正文流程配置信息
     * customVarConfig 扩展参数,数据的可能的来源. 1.表单元素 2.流程变量 3.决策项 4.环节变量 5.群组自定义变量
     * processTrace 流程跟踪信息
     * processTemplate 流程模板信息
     * taskUser 当前任务用户信息
     * processNode 当前节点信息
     * docInstance 正文实例信息
     * docTemplateConfig 正文模板配置
     * numNodeConfig 节点编号配置
     * currentNodeOpinionConfig 当前节点意见域配置
     * customVarCandidateToRead 待阅候选人自定义参数
     * customVarCandidateToDo 待办候选人自定义参数
     * userBaseExAttr 用户基本信息扩展属性
     * userIdentity 用户身份信息
     * processStatusText 当前任务状态
     * ownerOrgDeep: 当前任务处理人所属组织全路径
     */
    templateInit(data, templateMethods) {
      this.configData = data;
      this.templateMethods = templateMethods;
    },
    /**
     * 表单提交区域加载成功后的事件
     * @param {Object} funcs 方法对象
     */
    afterLoadFormSubmit(funcs) {
      this.formSubmitFuncs = funcs;
    },
    // 根据配置规则加载子组件
    loadSubComponent(definitionKey) {
      // 执行规则，根据流程定义编码获取要加载的子组件信息
      const componentConfig = subFormConfig[definitionKey ?? this.processDefinitionKey];
      // 子表单组件
      this.subFormComponent = (resolve) => require([
        `@/views/procecss-form${componentConfig.form}`,
      ], resolve);
      // 头部插槽必要
      const {
        defineComponentObj = {},
        defineComponentObj: {
          componentForm,
        } = {},
      } = componentConfig;
      // 自定义扩展区域
      this.extendCollapse = componentConfig?.extendCollapse?.map((t) => ({
        ...t,
        component: t.componentForm ? () => import(`@/views/procecss-form${t.componentForm}`) : null,
      }));
      // 流程目标配置
      this.customTemplateConfig = {
        ...componentConfig,
        // 附件区域上传的空间code
        attachmentAreaSpaceCode: componentConfig.spaceCode ?? 'default',
        // 使用的模板
        templateComponent: isEmptyStr(this.taskId) ? `${componentConfig.template}-process` : componentConfig.template,
        // 新增属性，头部插槽必要
        defineComponentObj: {
          ...defineComponentObj,
          component: componentForm
            ? () => import(`@/views/procecss-form${componentForm}`)
            : null,
        },
      };
    },
    /**
     * 提供子表单form数据改变进行扩展表单数据的更改
     * @param formData
     */
    changeBizFormData(formData) {
      this.formDataForExtendCollapse = cloneDeep(formData);
    },
    /**
     * 自定义加载子组件完成事件
     */
    customAfterLoadSubform(formData) {
      // 将子组件的formData赋值给扩展组件的formData，用于扩展组件回显数据
      this.formDataForExtendCollapse = cloneDeep(formData);
    },
    /**
     * 监听扩展组件的数据变化，同步给基本信息组件，用于保存时将扩展组件的数据一起保存
     * @param data 扩展组件的数据
     */
    changeExtendCollapseData(data) {
      if (!isEmpty(data) && isObject(data)) {
        this.$refs.subform.bizSet(cloneDeep(data));
      }
    },
    /**
     * 自定义扩展组件校验方法
     */
    extendCollapseValidateFn() {
      return Promise.all(this.$refs.extendCollapse?.map((item) => item.validate()));
    },
    /**
     * 业务组件点击顶部提交按钮前调用的事件
     */
    bizBeforeClickFormSubmitBtn() {
      return this.$refs.subform.beforeClickSubmitBtn?.();
    },
    /**
     * 选中路径的回调
     * @param {Object} path 选中的路径
     */
    bizOnCheckPath(path) {
      return this.$refs.subform?.onCheckPath?.(path);
    },
    /**
     * 点击顶部发送按钮
     * @param {Object} button 按钮对象
     * @param {Array} fileList 附件列表
     */
    transceiver(button, fileList) {
      const { piid } = this.$refs.templateRef.commonCapabilities;
      if (this.customTemplateConfig.isDirectSend) {
        this.directTransceiver(button, piid, fileList);
      } else {
        this.chooseDeptTransceiver(button, piid, fileList);
      }
    },
    /**
     * 流程跟踪点击处理人获取用户信息进行展示
     * @param row 行数据
     * @param column 列数据
     */
    getUserInfo(row, column) {
      this.renderConfig = {
        // 要渲染的组件
        type: () => import('@/components/ProcessUserInfo'),
        // 组件的属性
        attrs: {
          row,
          column,
        },
      };
      this.renderVisible = true;
    },
    /*
     * 等待业务组件初始化完成后，调用业务组件的修改业务数据中的流程信息、切换正文模板的方法
     * @param data 切换后的业务流程数据
     * @param originalData 流程所属的原始的业务流程信息
     */
    switchFormDataUntilLoaded(data, originalData) {
      const isLoaded = this.$refs.subform?.loaded;
      if (isLoaded) {
        this.$refs.subform.switchBusinessProcess(data, originalData);
      } else {
        defer(this.switchFormDataUntilLoaded, data, originalData);
      }
    },
    /**
     * 监听切换文种事件
     * @param data 切换后的业务流程数据
     * @param originalData 流程所属的原始的业务流程信息
     */
    switchBusinessCode(data, originalData) {
      const { businessCode } = data;
      // 获取切换后的流程的稿纸头
      this.documentTitle = templateNames[businessCode];
      this.processDefinitionKey = businessCode;
      // 根据新的业务流程编码，重新加载对应的业务组件
      this.loadSubComponent(businessCode);
      this.$nextTick(() => {
        // 等待业务组件初始化完成后，调用业务组件的修改业务数据中的流程信息、切换正文模板的方法
        this.switchFormDataUntilLoaded(data, originalData);
      });
    },
    /**
     * 点击安排本周调研操作
     */
    weekPlanBtn() {
      try {
        this.openWeekPlan();
      } catch (error) {
        showErrorMsg(error);
      }
    },
    /**
     * 安排本周调研组件
     * @returns {Promise<any>} - 用户点击保存时 resolve，点击取消时 reject
     */
    openWeekPlan() {
      return new Promise((resolve, reject) => {
        // 表单数据跟部门信息
        const { formData, loginUserInfo, statisticsSubmit } = this.$refs.subform;
        this.renderConfig = {
          // 要渲染的组件
          type: () => import('@/views/procecss-form/components/WeekPlan'),
          attrs: {
            // 表单数据
            formData,
            // 登录人信息
            loginUserInfo,
          },
          on: {
            // 保存成功
            saveSuccess: (val) => {
              this.renderVisible = false;
              statisticsSubmit(val);
              resolve();
            },
            cancel: () => {
              this.renderVisible = false;
              reject();
            },
          },
        };
        // 打开组件弹窗
        this.renderVisible = true;
      });
    },
  },
};
</script>
